<?php
/**
 * Created by PhpStorm.
 * User: tom_xia
 * Date: 2017/11/30
 * Time: 17:14
 */

namespace app\cls\tom;

use app\cls\tom\log\DbLog;
use app\cls\tom\log\FileLog;

class Tools
{
    public static function auth_code($string, $operation = 'DECODE', $key = '', $expiry = 0) {
        /*
            动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
            加入随机密钥，可以令密文无任何规律，即便是原文和密钥完全相同，加密结果也会每次不同，增大破解难度。
            取值越大，密文变动规律越大，密文变化 = 16 的 $ckey_length 次方
            当此值为 0 时，则不产生随机密钥
        */
        $ckey_length = 4;
        $key = md5($key != '' ? $key : "fdsfdf43535svxfsdfdsfs"); // 此处的key可以自己进行定义，写到配置文件也可以
        $keya = md5(substr($key, 0, 16));

        $keyb = md5(substr($key, 16, 16));
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
        $cryptkey = $keya.md5($keya.$keyc);
        $key_length = strlen($cryptkey);
        // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，解密时会通过这个密匙验证数据完整性
        // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
        $string_length = strlen($string);

        $result = '';
        $box = range(0, 255);

        $rndkey = array();
        for($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }

        for($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }

        for($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }

        if($operation == 'DECODE') {
            if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
            // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
            return $keyc.str_replace('=', '', base64_encode($result));
        }
    }

    public static function get_client_ip($client = true)
    {

        if($client){
            if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) {
                $ip = getenv("HTTP_CLIENT_IP");
//                echo 1;
            } else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown")) {
                $ip = getenv("HTTP_X_FORWARDED_FOR");
//                echo 2;
            } else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown")) {
                $ip = getenv("REMOTE_ADDR");
//                echo 3;
            }
            else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) {
                $ip = $_SERVER['REMOTE_ADDR'];
//                echo 4;
            }
            else
                $ip = "unknown";
        }else{
            ob_start();
            passthru("/usr/sbin/ip addr | grep inet | grep -v inet6 | awk '{print $2}' | awk -F '/' '{print $1}' | grep -v 127 | xargs");
            $str = ob_get_clean();
            $ip = implode(",",explode(" ",$str));
        }
        return $ip;
    }

    /**
     * @desc 通过用户的uuid获得存储用户信息的redis hash表名或者redis list名称
     * @param   $uuid
     * @param   $prefix string redis key的前缀
     * @param   $nums int key个数
     * @param   $bit int hash后缀位数，其中$bit必须>=$hashNums的位数
     * @return string hash key
     */
    public static function getHashSuffixByUUID($uuid, $prefix = "", $nums = 100, $bit = 3)
    {
        if (strlen($nums) > $bit) {
            return null;
        }
        //1.获取uuid的循环冗余码,并取正整数
        $code = sprintf("%u", crc32($uuid));
        //2.获取hash表后缀
        $format = "%'0" . $bit . "d";
        if ($nums == 1) {
            $hashSuffix = 0;
        } else {
            $hashSuffix = sprintf($format, ($code % $nums));
        }
        return $prefix . $hashSuffix;
    }

    public static function random($length, $numeric = 0)
    {
        $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
        $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed));
        if ($numeric) {
            $hash = '';
        } else {
            $hash = chr(rand(1, 26) + rand(0, 1) * 32 + 64);
            $length--;
        }
        $max = strlen($seed) - 1;
        for ($i = 0; $i < $length; $i++) {
            $hash .= $seed{mt_rand(0, $max)};
        }
        return $hash;
    }

    public static function UUID($namespace = '')
    {
        $guid = '';
        $uid = uniqid("", true);
        $data = $namespace;
        $data .= isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : ""; //
        $data .= isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ""; //$_SERVER['HTTP_USER_AGENT'];
        $data .= isset($_SERVER['LOCAL_ADDR']) ? $_SERVER['LOCAL_ADDR'] : ""; //$_SERVER['LOCAL_ADDR'];
        $data .= isset($_SERVER['LOCAL_PORT']) ? $_SERVER['LOCAL_PORT'] : ""; //$_SERVER['LOCAL_PORT'];
        $data .= isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ""; //$_SERVER['REMOTE_ADDR'];
        $data .= isset($_SERVER['REMOTE_PORT']) ? $_SERVER['REMOTE_PORT'] : ""; //$_SERVER['REMOTE_PORT'];
        $hash = strtoupper(hash('ripemd128', $uid . $guid . md5($data)));
        $guid = substr($hash, 0, 8) .
            '-' .
            substr($hash, 8, 4) .
            '-' .
            substr($hash, 12, 4) .
            '-' .
            substr($hash, 16, 4) .
            '-' .
            substr($hash, 20, 12);
        return $guid;
    }

    /**
     * xss过滤
     * @param $str
     * @return string
     */
    public static function xssFilter($str)
    {
        return htmlspecialchars($str);
    }

    public static function bigintUUID()
    {
        if (function_exists("dk_get_next_id")) {
            return dk_get_next_id();
        } else {
            return false;
        }
    }
    /**
     * API调用限制
     *
     * @param $params array
     * [
     * "h"=>"每小时限制多少,天,时，分钟至少有一个不为空",
     * "m"=>"每分钟限制多少",
     * "d"=>"每天限制多少",
     * "api"=>"api名字",可空，为空自动获取接口文件名称
     * "identity"=>"限制标识,为空限制ip"
     * ]
     */
    public static function apiLimit(array $params)
    {
        if (empty($params["id"])) {
            $identity = static::get_client_ip();
        } else {
            $identity = $params["id"];
        }
        if (empty($params["api"])) {
            $debugInfo = debug_backtrace();
            $api = pathinfo($debugInfo[count($debugInfo)-1]["file"],PATHINFO_FILENAME);
            $params["api"] = $api;
        }
        $keyPre = "api_limit_{$params["api"]}_{$identity}_";
        $redis = SysResource::getRedis();
        if (!empty($params["m"])) {
            $minuteAllowNum = $params["m"];
            $key = $keyPre . "m";
            $minuteNum = $redis->incr($key);
            $redis->expire($key,60);
            if( $minuteNum > $minuteAllowNum ){
                Response::output(-1, "minute,api invoke already reach maximum limit!");
            }
        }
        if (!empty($params["h"])) {
            $hourAllowNum = $params["h"];
            $key = $keyPre . "h";
            $hourNum = $redis->incr($key);
            $redis->expire($key,3600);
            if ( $hourNum > $hourAllowNum) {
                Response::output(-1, "hour,api invoke already reach maximum limit!");
            }
        }
        if (!empty($params["d"])) {
            $dayAllowNum = $params["d"];
            $key = $keyPre . "d";
            $dayNum = $redis->incr($key);
            $redis->expire($key,86400);
            if ($dayNum > $dayAllowNum) {
                Response::output(-1, "day,api invoke already reach maximum limit!");
            }
        }
        return;
    }
    /**
     * 写日志
     * @param array $params
     * @param $type
     */
    public static function writeLog(array $params,$type = "DB"){
        //栈追踪
        $debugInfo = debug_backtrace();
        $debugMsg = "【FILE:".$debugInfo[0]["file"]." -- line:".$debugInfo[0]["line"]."】 ";
        $params["msg"] = empty($params["msg"]) ? "" : $params["msg"];
        $params["msg"] = $debugMsg . $params["msg"];
        if($type == "DB"){
            $log = new DbLog($params);
        }else{
            $log = new FileLog($params);
        }
        $log->write();
    }

    /**
     * ks3裁剪
     * @param $w
     * @param null $h
     * @param bool $isCut
     * @return string
     */
    public static function scaleString($w,$h=NULL,$isCut=false)
    {
        $base = "@base@tag=imgScale";
        $scaleString = $base."&w={$w}";
        if($isCut && $h){
            $scaleString .= "&h={$h}&c=1";
        }
        return $scaleString;
    }

    /**
     * 是否合法的uuid
     * @param $uuid
     * @return bool
     */
    public static function isLegalUUID($uuid)
    {
        //AE2CFBDD-89DF-B427-D837-4DCBDB6A
        if(preg_match('/^[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}/',$uuid)>0){
            return true;
        }
        return false;
    }

    //http请求

    /**
     * @param $url
     * @param array $data
     * @param string $type
     * @param bool $https
     * @param string $header
     * @return mixed
     * @throws \Exception
     */
    public static function curl($url, $data=[], $type="get", $https=false, $header="")
    {
        $ch = curl_init();//curl句柄
        curl_setopt($ch,CURLOPT_TIMEOUT,10);//5秒的超时时间
        if($type == "get"){
            $url = $url."?".http_build_query($data);
        }
        if($header){
            curl_setopt($ch,CURLOPT_HTTPHEADER ,$header);
        }
        curl_setopt($ch,CURLOPT_URL ,$url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER ,1);
        if($type == "post"){
            curl_setopt($ch,CURLOPT_POST,1);
            curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
        }
        if($https==true){
            curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,1);
            curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,1);
        }else{
            curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0);
            curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0);
        }
        $result =  curl_exec($ch);
        if($result && empty(curl_error($ch))){
            curl_close($ch);
            return $result;
        }else{
            $error = curl_error($ch);
            curl_close($ch);
            throw new \Exception("curl错误:".$error);
        }
    }
}